using System;
using System.Collections.Generic;
using System.Reflection;
using Unity.Extensions;
using Xunit;

namespace Unity.Extensions
{
    public class RefParametersInterfaceInterceptorTest
    {
        [Fact]
        public void RefClassParameter()
        {
            RecordingHandler handler = new RecordingHandler();
            MethodBase method = typeof(ISpyRef).GetMethod("RefClassParameter");
            Dictionary<MethodBase, List<IInterceptionHandler>> dictionary = new Dictionary<MethodBase, List<IInterceptionHandler>>();
            List<IInterceptionHandler> handlers = new List<IInterceptionHandler> { handler };
            dictionary.Add(method, handlers);

            ISpyRef result = WrapAndCreateType<ISpyRef, SpyRef>(dictionary);
            string refValue = "Hello, ";
            result.RefClassParameter(ref refValue);

            Assert.Equal("Hello, world!", refValue);
        }

        [Fact]
        public void RefValueType()
        {
            RecordingHandler handler = new RecordingHandler();
            MethodBase method = typeof(ISpyRef).GetMethod("RefValueType");
            Dictionary<MethodBase, List<IInterceptionHandler>> dictionary = new Dictionary<MethodBase, List<IInterceptionHandler>>();
            List<IInterceptionHandler> handlers = new List<IInterceptionHandler> { handler };
            dictionary.Add(method, handlers);

            ISpyRef result = WrapAndCreateType<ISpyRef, SpyRef>(dictionary);
            int refValue = 21;
            result.RefValueType(ref refValue);

            Assert.Equal(42, refValue);
        }

        static TInterface WrapAndCreateType<TInterface, TConcrete>(IEnumerable<KeyValuePair<MethodBase, List<IInterceptionHandler>>> handlers)
            where TConcrete : TInterface
        {
            Type wrappedType = InterfaceInterceptor.WrapInterface(typeof(TInterface));
            ILEmitProxy proxy = new ILEmitProxy(handlers);
            object target = Activator.CreateInstance(typeof(TConcrete));
            List<object> wrappedCtorArgs = new List<object> { proxy, target };
            return (TInterface)Activator.CreateInstance(wrappedType, wrappedCtorArgs.ToArray());
        }

        public interface ISpyRef
        {
            void RefClassParameter(ref string value);
            void RefValueType(ref int value);
        }

        sealed class SpyRef : ISpyRef
        {
            public void RefClassParameter(ref string value)
            {
                value += "world!";
            }

            public void RefValueType(ref int value)
            {
                value *= 2;
            }
        }
    }
}